﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace NFox.Pyeditor.Runtime
{
    public class Console : TextBox
    {

        public Console()
        {
            AcceptsReturn = true;
            TextWrapping = TextWrapping.Wrap;
            VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
            ContextMenu = null;
            FontFamily = new FontFamily("Consolas");
            FontSize = 13;
            BorderThickness = new Thickness();
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, (s, e) => Cut(), CanCut));
            InputBindings.Add(new InputBinding(ApplicationCommands.Cut, new KeyGesture(Key.X, ModifierKeys.Control)));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, (s, e) => Copy(), CanCopy));
            InputBindings.Add(new InputBinding(ApplicationCommands.Copy, new KeyGesture(Key.C, ModifierKeys.Control)));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, (s, e) => Paste(), CanPaste));
            InputBindings.Add(new InputBinding(ApplicationCommands.Paste, new KeyGesture(Key.V, ModifierKeys.Control)));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, (s, e) => SelectedText = "", CanDelete));
            InputBindings.Add(new InputBinding(ApplicationCommands.Delete, new KeyGesture(Key.Delete)));

            Write(">>>");
        }

        int _pos;
        bool _notselectnull;
        bool _canchange;

        private void CanCut(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _notselectnull && _canchange;
        }

        private void CanCopy(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _notselectnull;
        }

        private void CanPaste(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _canchange;
        }

        private void CanDelete(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _canchange;
        }

        protected override void OnSelectionChanged(RoutedEventArgs e)
        {
            base.OnSelectionChanged(e);
            _notselectnull = SelectionLength > 0;
            _canchange = SelectionStart >= _pos;
        }

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);
            int id = Text.LastIndexOf('\n');

            if (e.KeyboardDevice.Modifiers == ModifierKeys.Control)
            {
                switch (e.Key)
                {
                    case Key.X:
                    case Key.V:
                        e.Handled = SelectionStart < _pos;
                        return;
                    case Key.C:
                        return;
                }
            }

            switch (e.Key)
            {
                case Key.Up:
                case Key.Down:
                case Key.Left:
                case Key.Right:
                    break;
                case Key.Back:
                    e.Handled = SelectionStart < _pos || (SelectionStart == _pos && SelectionLength == 0);
                    break;
                case Key.Enter:
                    e.Handled = true;
                    if (SelectionStart >= _pos && SelectionLength == 0)
                    {
                        Eval(Text.Substring(_pos));
                        Select(Text.Length, 0);
                        ScrollToEnd();
                    }
                    break;
                default:
                    e.Handled = SelectionStart < _pos;
                    break;
            }

            System.Windows.Forms.Application.DoEvents();

        }


        #region IO

        public void Write(string message)
        {
            Text += message;
            _pos = Text.Length;
            Select(Text.Length, 0);
            ScrollToEnd();
        }

        public void WriteNewLine()
        {
            Write(Environment.NewLine);
        }

        List<string> _buffer = new List<string>();
        bool _eof;

        static Regex _regex = new Regex(@"^\s*(.*?):\s*$");

        public bool IsRead { get; set; }

        private void Eval(string message)
        {

            if (IsRead)
            {
                ConsoleStream.Enqueue(message + Environment.NewLine);
                IsRead = false;
                WriteNewLine();
                return;
            }

            bool newline = true;
            if (_eof)
            {
                if (message == "")
                {
                    _eof = false;
                }
            }
            else if (_regex.IsMatch(message))
            {
                _eof = true;
            }

            if (newline)
                WriteNewLine();

            if (message != "")
                _buffer.Add(message);

            if (!_eof && _buffer.Count > 0)
            {
                var e = new CommandEventArgs(string.Join("\r\n", _buffer));
                _buffer.Clear();
                try
                {
                    CommandStartting?.Invoke(this, e);
                }
                catch (Exception ex)
                {
                    Write(ex.Message);
                }
                finally
                {
                    CommandEnded?.Invoke(this, e);
                }
            }

            if (Text[Text.Length - 1] != '\n')
                WriteNewLine();

            Write(_eof ? "   " : ">>>");

        }

        public delegate void CommandHandle(object sender, CommandEventArgs e);
        public event CommandHandle CommandStartting;
        public event CommandHandle CommandEnded;

        #endregion

    }

    public class CommandEventArgs : Exception
    {
        public string Command { get; }
        public object Result { get; set; }
        public CommandEventArgs(string cmd)
        {
            Command = cmd;
        }
    }

}
